import pygame
from pygame.locals import *  # pygame已经帮你设置好的各种游戏常量
import time
import random

# 类是具有相同的属性和方法的对象的集合


class HeroPlane:
    ''' 放自己的小飞机'''

    # self是个参数代表类的实例对象,screen_temp也代表了一个参数(窗口对象的参数)
    def __init__(self, screen_temp):
        self.x = 208
        self.y = 444
        # 把具有窗口对象的这个属性赋给了self(一个实例化的对象)，self.screen是我用来接收的，才能装入图片
        self.screen = screen_temp

        # self.image不是什么属性,pygame.image.load()返回一个对象,当然是给self(实例对象)啦
        self.image = pygame.image.load("C:/Users/Madam/Desktop/Python/meme/me.png")

        # 存放发射子弹的列表
        self.bullet_list = []

    def display(self):
        ''' 绘制我的小飞机到窗口中, blit(图像,(坐标x,坐标y))是绘制,传输图像语句，把图片绑定到我的窗口对象上 '''
        self.screen.blit(self.image, (self.x, self.y))

        # 遍历子弹列表
        for biubiu in self.bullet_list:

            # 绘制子弹,这里的display()是在Bullet类中定义的
            biubiu.display()

            # 判断条件释放资源
            if biubiu.fire('up'):
                self.bullet_list.remove(biubiu)  # biubiu这个子弹类Bullet的实例对象(见第57行)

    def move_left(self):
        ''' 左移动,并判断防止越界 '''
        self.x -= 5
        if self.x < 0:
            self.x = 0

    def move_right(self):
        ''' 右移动,并判断防止越界 '''
        self.x += 5
        if self.x > 415:
            self.x = 415

    def move_up(self):
        '''上移动,并防止越界'''
        self.y -= 5
        if self.y < 0:
            y = 0

    def move_down(self):
        '''.下移动，并防止越界'''
        self.y += 5
        if self.y >= 444:
            self.y = 444

    def fire(self):
        self.bullet_list.append(Bullet(self.screen, self.x + 3, self.y - 10))
        '''这里是子弹的实例化过程,以实例化的飞机的坐标和窗口作为子弹的坐标和窗口'''
    def boom(self):
        '''子弹的爆炸效果'''

        for i in range(4):
            # 循环载入爆炸效果序列帧
            self.boom_list.append(pygame.image.load('./images/boom' + str(i) + '.png'))

        for boom in self.boom_list:
            # 在游戏窗口中循环子弹爆炸序列帧，形成爆炸动画
            self.screen.blit(boom, (self.x, self.y))


class Bullet:
    '''子弹类'''

    # 在这里x,y这两个是属性,如果把属性写在这个位置的括号里,代表着可以自己设定其值
    def __init__(self, screen_temp, x, y):
        self.x = x + 50
        ''' x的值由你来设定(我们让该方法在飞机绘制那里得到调用,那么就可以把飞机的x传进来这里),
        然后赋值给self.x也就是给你的实例化对象(在这个类里是子弹)'''
        self.y = y
        # 绘制窗口
        self.screen = screen_temp
        # 传入子弹的样式
        self.image = pygame.image.load("C:/Users/Madam/Desktop/Python/meme/pd.png")
        # 创建一个空的爆炸效果序列帧列表
        self.boom_list = []

    def display(self):
        self.screen.blit(self.image, (self.x, self.y))

    def fire(self, direction):
            # 若子弹向上飞
        if direction == 'up':
            # 上飞速度
            self.y -= 8
            # 判断子弹是否飞出游戏窗口上界
            if self.y <= -10:
                return True

        # 若子弹向下飞
        elif direction == 'down':
            # 下飞速度
            self.y += 4
            # 判断子弹是否飞出游戏窗口下界
            if self.y >= 580:
                return True

    def boom(self):
        '''子弹的爆炸效果'''

        for i in range(4):
            # 循环载入爆炸效果序列帧
            self.boom_list.append(pygame.image.load('C:/Users/Madam/Desktop/Python/meme/boom' + str(i) + '.png'))

        for boom in self.boom_list:
            # 在游戏窗口中循环子弹爆炸序列帧，形成爆炸动画
            self.screen.blit(boom, (self.x, self.y))


class EnemyPlane:
    """敌机的类98*124"""

    def __init__(self, screen_temp):
        self.x = random.choice(range(414))
        self.y = -124
        self.screen = screen_temp
        # 从三种敌机形象中随机选择一个
        self.image = pygame.image.load('C:/Users/Madam/Desktop/Python/meme/e' + str(random.choice(range(3))) + '.png')
        # 获取敌机surface的宽和高
        self.width, self.height = self.image.get_size()
        # 为敌机定义一个空的子弹夹
        self.enemy_bullet_list = []

    # 绘制敌机和子弹
    def display(self):

        # 绘制敌机
        self.screen.blit(self.image, (self.x, self.y))
        # 绘制子弹
        for ebullet in self.enemy_bullet_list:
            ebullet.display()
            if ebullet.fire('down'):
                self.enemy_bullet_list.remove(ebullet)

    def move(self, hero):

        # 下飞速度
        self.y += 3

        # 遍历玩家子弹
        for bullet in hero.bullet_list:
            # 检测玩家子弹与敌机是否碰撞(玩家子弹坐标是否进入敌机碰撞检测区,一个矩形80*40)
            if bullet.x > self.x + 12 and bullet.x < self.x + 92 and bullet.y > self.y + 20 and bullet.y < self.y + 60:
                # 玩家子弹击中敌机，显示子弹的爆炸效果
                bullet.boom()
                # 玩家子弹与敌机同归于尽
                hero.bullet_list.remove(bullet)

                return True

        # 判断敌机是否飞出屏幕
        if self.y >= 580:
            return True

        # 遍历敌机子弹
        for bullet in self.enemy_bullet_list:
            # 检测敌机子弹与玩家飞机是否碰撞
            if bullet.x > hero.x + 12 and bullet.x < hero.x + 92 and bullet.y > hero.y + 20 and bullet.y < hero.y + 60:
                # 敌机子弹击中玩家，显示子弹的爆炸效果
                bullet.boom()
                # 敌机子弹与玩家两败俱伤
                self.enemy_bullet_list.remove(bullet)

    def fire(self):
        '''敌机随时发射子弹'''
        if random.choice(range(60)) == 10:
            self.enemy_bullet_list.append(Bullet(self.screen, self.x, self.y + self.height))


def main():
    '''主程序函数'''

    # 初始化pygame各模块
    pygame.init()

    # 1. 创建窗口:set_mode(分辨率=(0,0),标志=0,深度=0),设置标题
    screen = pygame.display.set_mode((512, 568), 0, 0)
    pygame.display.set_caption('我的飞机大战')

    # 2. 创建一个游戏背景图片(512*2250)
    background = pygame.image.load("C:/Users/Madam/Desktop/Python/meme/background.jpg")

    # 4.创建一个玩家飞机对象,显示英雄玩家
    hero = HeroPlane(screen)  # 类的实例化,需要传一个参数,传个窗口给它绘制,并不是会把整个窗口绘制满,而是看你自己hero.image多大

    # 5.定义用于存放敌机列表
    enemy_list = []

    # 6. 绘制位图
    m = -1682  # 初始化游戏背景图片标轴y的值
    while True:
        screen.blit(background, (0, m))  # 3.绑定窗口和画面，在重复图片位置的时候就不会出现那种图片覆盖的效果，滚动画面
        m += 3
        if m >= -500:
            m = -1682

    # 7.绘制玩家飞机和子弹
        # 子弹绘制的方法放在了绘制飞机的方法中引用了,所以不必在bullet.display()
        hero.display()
        '''question,hero这个实例对象有display()绘制方法了为什么还要用display语句?因为这不是运用了display语句,是调用其方法的语句!'''

    # 8.键盘控制（负责移动玩家）
        key_control(hero)

    # 随机产生一个敌机并放入敌机列表
        if random.choice(range(60)) == 10:
            enemy_list.append(EnemyPlane(screen))

        # 遍历敌机
        for enemy in enemy_list:
            # 绘制敌机
            enemy.display()
            # 敌机随时发射子弹
            enemy.fire()

            # 移动敌机并检测其是否被击中或飞出屏幕
            if enemy.move(hero):
                enemy_list.remove(enemy)

        # 更新屏幕显示
        pygame.display.update()

        # 画面更新时间控制
        time.sleep(0.04)


# 显然hero_temp也是一个变量名,方便调用
def key_control(hero_temp):
    ''' 键盘控制函数 '''

# 实时事件循环,会根据事件产生的顺序依次地进行不同的操作,如按键按下,按键释放,关闭窗口等等事件
    for event in pygame.event.get():
        # 判断是否是点击了退出按钮
        if event.type == QUIT:
            print("exit")
            exit()
    pressed_keys = pygame.key.get_pressed()  # 遍历所有pygame中的操作按键,并用 pressed_keys接受就能够明确了A代表右移动等等
    # 检测是否按下a或者left键
    if pressed_keys[K_LEFT] or pressed_keys[K_a]:  # K_LEFT or K_a 这两个表达是固定的
        print('left')
        hero_temp.move_left()
    # 检测是否按下d或者right键
    elif pressed_keys[K_RIGHT] or pressed_keys[K_d]:
        print('right')
        hero_temp.move_right()
        '''代表着调用其他方法,key_control是这个函数的名称,hero_temp则代表这个函数,
        这样子实现把别的方法添加到了自己的函数方法里面
        如果你用key_control.move.left(),别忘了key_control里面也有自己的方法,所以会造成混乱
        '''
    # 检测是否按下w或者up键
    if pressed_keys[K_UP] or pressed_keys[K_w]:
        print('up')
        hero_temp.move_up()
    elif pressed_keys[K_DOWN] or pressed_keys[K_s]:
        print('down')
        hero_temp.move_down()
    # 检查是否是空格键
    if pressed_keys[K_SPACE]:
        print('space')
        hero_temp.fire()


''' 可以重复响应一个被一直按住的键，单位是毫秒
    pygame.key.set_repeat(10)
'''


# 判断当前是否是主程序，若是就执行主程序。
if __name__ == "__main__":
    main()
